package main

import (
	"errors"
	"flag"
	"fmt"
	"io/fs"
	"log"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
)

func homedir() (string, error) {
	if dir := os.Getenv("HOME"); dir != "" {
		return dir, nil
	}
	if u, err := user.Current(); err == nil && u.HomeDir != "" {
		return u.HomeDir, nil
	}
	return "", errors.New("can't find user home directory; $HOME is empty")
}

// findGoExists 在baseDir目录下查找已存在的go版本, 返回版本列表和默认的go版本的编号, 如果是-1则表示列表中不存在默认的go版本
func findGoExists(baseDir string, dirs []fs.DirEntry) ([]string, string) {
	cmd := exec.Command("go", "env", "GOVERSION")
	bs, _ := cmd.Output()
	defv := strings.TrimSpace(string(bs))
	gocmdName := "go"
	if runtime.GOOS == "windows" {
		gocmdName = "go.exe"
	}

	versions := make([]string, 0, len(dirs))
	for i := range dirs {
		name := dirs[i].Name()
		if name == "go" {
			continue
		}
		exe := filepath.Join(baseDir, name, "bin", gocmdName)
		_, err := os.Stat(exe)
		if err != nil {
			continue
		}
		versions = append(versions, name)
	}
	return versions, defv
}

func replaceGoVersion(baseDir, version string) error {
	fmt.Printf("\n1. 正在将默认版本切换到(%s)\n", version)
	goDefaultVersion := filepath.Join(baseDir, "go")
	if err := os.RemoveAll(filepath.Join(baseDir, "go")); err != nil {
		return fmt.Errorf("failed to remove %v: %v", goDefaultVersion, err)
	}
	fmt.Printf("2. 删除默认版本(%s)成功\n", goDefaultVersion)

	goSelectedVersion := filepath.Join(baseDir, version)

	// 使用软链接
	if isSymlink() {
		if err := os.Symlink(goSelectedVersion, goDefaultVersion); err != nil {
			return fmt.Errorf("failed to create symlink %v => %v: %v", goSelectedVersion, goDefaultVersion, err)
		}
		fmt.Printf("3. 创建软链接 %v => %v 成功\n", goSelectedVersion, goDefaultVersion)
		return nil
	} else {
		// 使用复制
		if err := os.CopyFS(goDefaultVersion, os.DirFS(goSelectedVersion)); err != nil {
			return fmt.Errorf("failed to copy %v => %v: %v", goSelectedVersion, goDefaultVersion, err)
		}
		fmt.Printf("3. 复制 %v => %v 成功\n", goSelectedVersion, goDefaultVersion)
	}
	return nil
}

const downloadUsage = `
如果在未找到需要的golang版本, 请参考 https://go.dev/doc/manage-install
如果在中国，请参考 https://golang.google.cn/doc/manage-install

`

const envUsage = `
# go
export GOBIN=$HOME/go/bin
export PATH=$PATH:$HOME/sdk/go/bin:$GOBIN

`

var useSymlink = flag.Bool("symlink", false, "使用软链接而不是复制, 默认为false, 可以使用环境变量GOVM_USE_SYMLINK=1来启用")

func isSymlink() bool {
	if *useSymlink {
		return true
	}
	return os.Getenv("GOVM_USE_SYMLINK") == "1"
}

func main() {
	flag.Parse()
	home, err := homedir()
	if err != nil {
		log.Fatalf("failed to get home directory: %v", err)
	}
	sdk := filepath.Join(home, "sdk")
	dirs, err := os.ReadDir(sdk)
	if err != nil {
		log.Fatalf("failed to read directory %v: %v", sdk, err)
	}
	vers, defv := findGoExists(sdk, dirs)
	if len(vers) == 0 {
		fmt.Printf("no go version found in %v\n", sdk)
		os.Exit(1)
	}
	fmt.Printf("有 %d 个golang版本可供选择(默认为 %s)\n\n", len(vers), filepath.Join(sdk, "go"))
	fmt.Printf("  选择\t版本\t使用软链接(%t)\n", isSymlink())
	fmt.Println("----------------------")
	indexPrefix := func(name string) string {
		if defv == name {
			return "*"
		}
		return " "
	}
	for i := range vers {
		fmt.Printf("%s %d\t%s\n", indexPrefix(vers[i]), i, vers[i])
	}
	fmt.Print(downloadUsage)
	fmt.Printf("输入选择的编号，或者按<CTRL + C>终止: ")

	var seleted int
	_, err = fmt.Scanf("%d", &seleted)
	if err != nil {
		os.Exit(1)
	}
	v := vers[seleted]
	if v != defv {
		if err := replaceGoVersion(sdk, v); err != nil {
			fmt.Printf("替换golang版本 %s => %s失败: %s\n", defv, v, err)
			os.Exit(1)
		}
		fmt.Println("\n请确保环境变量设置正确，像如下示例(bash)：")
		fmt.Print(envUsage)

	}
}
